/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.cax.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.cax.domain.CaxConvertInfo;
import com.comac.ins.cax.domain.CaxFileInfo;
import com.comac.ins.cax.domain.bo.CaxConvertInfoBo;
import com.comac.ins.cax.domain.vo.CaxConvertInfoVo;
import com.comac.ins.cax.enums.CaxConvertFileType;
import com.comac.ins.cax.enums.CaxConvertStatus;
import com.comac.ins.cax.enums.CaxConvertType;
import com.comac.ins.cax.mapper.CaxConvertInfoMapper;
import com.comac.ins.cax.service.ICaxConvertInfoService;
import com.comac.ins.cax.service.ICaxFileInfoService;
import com.comac.ins.cax.service.strategy.ConversionStrategy;
import com.comac.ins.cax.service.strategy.impl.ConversionContext;
import com.comac.ins.cax.service.strategy.impl.ConversionStrategyFactory;
import com.comac.ins.cax.utils.FileUtils;
import com.comac.ins.common.core.utils.ServletUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.common.satoken.utils.LoginHelper;
import com.comac.ins.system.domain.vo.SysClientVo;
import com.comac.ins.system.service.ISysClientService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;

/**
 * @author yk
 * @desc 转换Service
 * @date 2024-06-18 16:51
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CaxConvertInfoServiceImpl implements ICaxConvertInfoService {

    private final CaxConvertInfoMapper caxConvertInfoMapper;

    private final ICaxFileInfoService caxFileInfoService;

    private final ISysClientService sysClientService;

    @Override
    public CaxConvertInfoVo convert(MultipartFile file, String convertTypeStr,
                                    String inputTypeStr, String outputTypeStr) {
        if (!FileUtils.isValidFileTypeBySuffix(inputTypeStr, file)) {
            throw new RuntimeException("输入文件格式和上传文件不匹配");
        }
        // 获取用户来源
        String clientId = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
        SysClientVo sysClientVo = sysClientService.queryByClientId(clientId);
        CaxConvertType caxConvertType = CaxConvertType.valueOf(convertTypeStr);
        CaxConvertFileType inputType = CaxConvertFileType.valueOf(inputTypeStr);
        CaxConvertFileType outputType = CaxConvertFileType.valueOf(outputTypeStr);
        Assert.notNull(caxConvertType, "conversionType字段非法");
        Assert.notNull(inputType, "inputType字段非法");
        Assert.notNull(outputType, "outputType字段非法");

        ConversionStrategy strategy = ConversionStrategyFactory.getConversionStrategy(caxConvertType, inputType, outputType);
        // 附件上传，db数据存储
        CaxFileInfo caxFileInfo = caxFileInfoService.uploadFile(file);
        if (ObjectUtils.isEmpty(caxFileInfo)) {
            throw new RuntimeException("文件上传失败");
        }
        CaxConvertInfo caxConvertInfo = new CaxConvertInfo();
        caxConvertInfo.setConvertType(convertTypeStr);
        caxConvertInfo.setInputType(inputTypeStr);
        caxConvertInfo.setOutputType(outputTypeStr);
        caxConvertInfo.setSourceFileId(caxFileInfo.getId());
        caxConvertInfo.setSourceFileName(caxFileInfo.getName());
        caxConvertInfo.setSource(ObjectUtils.isEmpty(sysClientVo) ? "" : sysClientVo.getClientKey());
//        caxConvertInfo.setStatus(CaxConvertStatus.IN_PROGRESS.getCode());
//        caxConvertInfoMapper.insert(caxConvertInfo);
//        CaxConvertInfoVo vo = new CaxConvertInfoVo();
//        BeanUtils.copyProperties(caxConvertInfo, vo);
//        // 异步执行转换过程
//        dealConvert(strategy, caxFileInfo.getPath(), caxConvertInfo);
        Boolean flag = dealConvert(strategy, caxFileInfo.getPath());
        String status = flag ? CaxConvertStatus.SUCCESS.getCode() : CaxConvertStatus.FAILURE.getCode();
        caxConvertInfo.setStatus(status);
        caxConvertInfoMapper.insert(caxConvertInfo);
        CaxConvertInfoVo vo = new CaxConvertInfoVo();
        BeanUtils.copyProperties(caxConvertInfo, vo);
        return vo;
    }

    public Boolean dealConvert(ConversionStrategy strategy, String filePath) {
        try {
            ConversionContext context = new ConversionContext(strategy);
            return context.executeConversion(filePath);
        } catch (Exception e) {
            log.error("文件转换异常 => {}", filePath);
            return false;
        }
    }

//    @Async
//    public void dealConvert(ConversionStrategy strategy, String filePath, CaxConvertInfo caxConvertInfo) {
//        try {
//            ConversionContext context = new ConversionContext(strategy);
//            boolean flag = context.executeConversion(filePath);
//            // 写入主表转换结果
//            if (flag) {
//                caxConvertInfo.setStatus(CaxConvertStatus.SUCCESS.getCode());
//            } else {
//                caxConvertInfo.setStatus(CaxConvertStatus.FAILURE.getCode());
//            }
//            caxConvertInfoMapper.updateById(caxConvertInfo);
//        } catch (Exception e) {
//            // 写入主表转换失败结果
//            caxConvertInfo.setStatus(CaxConvertStatus.FAILURE.getCode());
//            caxConvertInfoMapper.updateById(caxConvertInfo);
//            log.error("文件转换异常 => {}", filePath);
//        }
//    }

    @Override
    public CaxConvertInfoVo queryById(Long id) {
        return caxConvertInfoMapper.selectVoById(id);
    }

    @Override
    public TableDataInfo<CaxConvertInfoVo> queryPageList(CaxConvertInfoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<CaxConvertInfo> lqw = buildQueryWrapper(bo);
        Page<CaxConvertInfoVo> result = caxConvertInfoMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    private LambdaQueryWrapper<CaxConvertInfo> buildQueryWrapper(CaxConvertInfoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<CaxConvertInfo> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getConvertType()), CaxConvertInfo::getConvertType, bo.getConvertType());
        lqw.eq(StringUtils.isNotBlank(bo.getInputType()), CaxConvertInfo::getInputType, bo.getInputType());
        lqw.eq(StringUtils.isNotBlank(bo.getOutputType()), CaxConvertInfo::getOutputType, bo.getOutputType());
        lqw.eq(StringUtils.isNotBlank(bo.getSource()), CaxConvertInfo::getSource, bo.getSource());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CaxConvertInfo::getStatus, bo.getStatus());
        lqw.like(StringUtils.isNotBlank(bo.getSourceFileName()), CaxConvertInfo::getSourceFileName, bo.getSourceFileName());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            CaxConvertInfo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.orderByAsc(CaxConvertInfo::getCreateTime);
        return lqw;
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        return caxConvertInfoMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public void downloadSource(HttpServletResponse response, Long id) throws IOException {
        CaxConvertInfo caxConvertInfo = caxConvertInfoMapper.selectById(id);
        CaxFileInfo caxFileInfo = caxFileInfoService.queryById(caxConvertInfo.getSourceFileId());
        downloadFile(response, caxFileInfo.getPath(), caxFileInfo.getName());
    }

    @Override
    public void downloadTarget(HttpServletResponse response, Long id) throws IOException {
        CaxConvertInfo caxConvertInfo = caxConvertInfoMapper.selectById(id);
        if (caxConvertInfo.getStatus().equals(CaxConvertStatus.IN_PROGRESS.getCode())) {
            throw new RuntimeException("转换中数据，不能下载");
        } else if (caxConvertInfo.getStatus().equals(CaxConvertStatus.FAILURE.getCode())) {
            throw new RuntimeException("转换失败数据，不能下载");
        } else if (!caxConvertInfo.getStatus().equals(CaxConvertStatus.SUCCESS.getCode())) {
            throw new RuntimeException("转换数据状态异常，不能下载");
        }
        CaxFileInfo caxFileInfo = caxFileInfoService.queryById(caxConvertInfo.getSourceFileId());
        String fileTypeBySuffix = FileUtils.getFileTypeBySuffix(caxConvertInfo.getOutputType()).replace(".", "");
        String filePath = FileUtils.changeFileExtension(caxFileInfo.getPath(), fileTypeBySuffix);
        String fileName = FileUtils.changeFileExtension(caxFileInfo.getName(), fileTypeBySuffix);
        downloadFile(response, filePath, fileName);
    }

    /**
     * 文件下载
     *
     * @param response response
     * @param filePath 文件路径
     * @param fileName 文件名称
     * @throws IOException 异常
     */
    public void downloadFile(HttpServletResponse response, String filePath, String fileName) throws IOException {
        response.setContentType("application/octet-stream");
        // 设置Content-Disposition头，用于指示浏览器将以附件的形式下载文件，而不是显示在浏览器中
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

        // 读取文件并写入输出流
        File file = new File(filePath);
        if (file.exists()) {
            OutputStream out = null;
            try (FileInputStream fis = new FileInputStream(file)) {
                out = response.getOutputStream();

                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
            } finally {
                if (out != null) {
                    out.flush();
                    out.close();
                }
            }
        } else {
            log.error("文件下载异常 => {}", filePath);
            throw new RuntimeException("文件下载失败");
        }
    }
}
